perm filename INFREE.MAC[IP,SYS] blob sn#680206 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-INET>INFREE.MAC.40301 29-Jan-82 15:11:42, Edit by CLYNN
; Updated for IP release 3
;[BBNF]<401-INET>INFREE.MAC.16, 14-Jul-81 11:54:00, Ed: CLYNN
; Bug: Returning core beyond end of section & loop in ODDBLK chain
;     (End-of-list matching end-of-memory (INTSEC,,0) in SPUT4)
;[BBND]<401-INET>INFREE.MAC.15, 10-Feb-81 10:19:37, Ed: CLYNN
;ODDBLK is not in INTSEC
;	GCARVE+2	MOVEI PRV,ODDBLK	XMOVEI PRV,ODDBLK
;	       3	SETSEC PRV,INTSEC
;BLK is used as a temp which destroies the routine return value; use T3
;	GCARV1+4	LOAD BLK,FLIST,(BLK)	LOAD T3,FLIST,(BLK)
;	       5	STOR BLK,FLIST,(PRV)	STOR T3,FLIST,(PRV)
;T1 is not initialized to BLK
;	CSPLIT		LOAD T3,FSIZE,(BLK)	LOAD T3,FSIZE,(BLK)
;						MOVE T1,BLK
;T2 is not initialized to SIZ, replace T2 by SIZ
;	CSPLIT+3	STOR T2,FSIZE,(BLK)	STOR SIZ,FSIZE,(BLK)
;	       4	SUBI T3,UBLKSZ(T2)	SUBI T3,UBLKSZ(SIZ)
;[BBND]SNARK:<401-INET>INFREE.MAC.14, 23-Jan-81 12:35:30, Ed: TAPPAN

	SEARCH	INPAR,PROLOG
	TTITLE	INFREE
	SUBTTL	Internet Free Storage Routines, Wm W. Plummer 9FEB77
	SWAPCD

COMMENT	!
	Routines to manage the INT freestorage area.  Designed to
	provide quick access to commonly used block sizes.

* FREINI ...  4 ...... Initialize the freestorage package
* FREAVL ...  4 ...... Returns the amount of available free storage

* RETBLK ...  5 ...... Return a block to free storage
  RETBK0 ...  5 ...... Internal routine to return a block
  RETB .....  6 ...... Subr. Called by INTFRE and the user
  SPUT .....  7 ...... Chain a block onto a list

* GETBBK ...  9 ...... Get biggest block of free storage
  GETBB0 ... 10 ...... Workhourse for GETBBK
* GETBLK ... 11 ...... Get a specific size block of free storage
  GETBK0 ... 12 ...... Workhorse for GETBLK
  GETB ..... 13 ...... Subr. called by INTFRE and the user
  GCARVE ... 14 ...... Carve an oddblock into required size
  CSPLIT ... 15 ...... Subr. actually split a block
  BULKCV ... 16 ...... Carve a block out of bulk area
  GC ....... 18 ...... Garbage collect the odd blocks list
  LCOPY .... 19 ...... Copy one list onto another
  RETLST ... 20 ...... Return a list of blocks to free storage

  CHKBLK ... 20 ...... Check validity of block returned by user

  HASH ..... 21 ...... Local hash function
  HASHX .... 22 ...... Returns hash indext to INTBLK table

******* N.B.  The ACs named BLK, SIZ, and LST are used globally
	      within this file.
	!

; AC redefinitions:

IF1 <
BLK=1+NTEMPS+NLOCLS-3		; Holds pointer to block in question
SIZ=1+NTEMPS+NLOCLS-2		; Holds size of block
LST=1+NTEMPS+NLOCLS-1		; Holds pointer to a list of blocks
NLOCLS==NLOCLS-3
>

; Note: INTBLK contains FSIZE,,FLIST;  i.e. a header w/o FNEXT
; Definitions for FREE block header structure:

DEFSTR(FSIZE,0,17,18)		; (User) Block size (excluding UBLKSZ)
DEFSTR(FLIST,0,35,18)		; List of other blocks
DEFSTR(FNEXT,1,35,18)		; Next block higher in memory
FBLKSZ==2			; Size of a FREE block header


; Definitions for USED block header structure:

DEFSTR(USIZE,0,17,35)		; (User) Block size (excluding UBLKSZ header)
DEFSTR(UHASH,0,35,18)		; Hash. Guards against user clobbering
UBLKSZ==1			; Size of a USED block header



; Block size quantitization -- all blocks except very short ones
; are forced to be a multiple of this size.

BSMALL==10			; Don't quantize this size or smaller
BSQUAN==10			; Must be a power of 2

; FREINI	Initialize the Free Storage area

;	CALL FREINI
;Ret+1:	Always.

FREINI::SETZM ODDBLK		; No odd size blocks returned yet

	MOVE T1,[INTBLK,,INTBLK+1]	; Set for a BLT
	SETZM INTBLK		; Clear 1st word of table
	BLT T1,INTBLK+NFIXED-1	; Clear rest

IFNKA <	MOVE T2,[INTSEC,,INTFRE]>; Pointer to the INT free area
IFKA <	MOVEI T2,INTFRE>
	SKIPN EXADFL		; Machine has extended addressing?
	 HRRZS T2		; No.  INTFRE is in section 0
	MOVEM T2,BULKST		; Beginning of bulk storage
	ADDI T2,INTFSZ-1
	MOVEM T2,BULKND		; End of the bulk storage
	MOVEI T4,INTFSZ
	MOVEM T4,INTFSP		; Amount of free space available

	SETZM MRGFLG		; No block merging possible yet.

	SETZM INTSVC		; Count of scavenges done
	SETZM INTSVT		; TODCLK filter of excessive BUGINF's
	SETZM INTSVR		; Scavenge request flag

	MOVEI T1,FRELCK		; Free storage lock
	CALLRET CLRLCK		; Initialize it



; FREAVL	Returns amount of free storage available

;	CALL FREAVL
;Ret+1:	Always, number of words in T1

FREAVL::MOVE T1,INTFSP
	RET

; RETBLK	Return a block to free area

;T1/	(Extended) Pointer to the user portion of the block
;
;	CALL RETBLK
;Ret+1:	Always

RETBLK::XMOVEI T3,-UBLKSZ(T1)	; Pointer to free header
	XMOVEI T2,[PUSH P,BLK	; Save ACs which will be
		PUSH P,SIZ	; as globals
		PUSH P,LST
		MOVE BLK,T1	; Set block pointer
		LOAD SIZ,USIZE,(BLK)	; and size (excluding header)
		CALL RETBK0	; Do the returning
		POP P,LST
		POP P,SIZ
		POP P,BLK
		RET]
	MOVEI T1,FRELCK		; Lock to set
	CALLRET LCKCAL		; Lock and call.



; RETBK0	Workhorse for above.  Called with NOINT & FRELCK set.

;BLK/	(Extended) Pointer to the block to return
;SIZ/	Size of the block, excluding header
;
;	CALL RETBK0
;Ret+1:	Always

RETBK0:	CALL CHKBLK		; Make sure header not crashed by user
	MOVEI T1,UBLKSZ(SIZ)	; Total length of the block
	ADDM T1,INTFSP		; Amount of free storage now available
	ADD T1,BLK		; Next location in memory
	STOR T1,FNEXT,(BLK)	; Save in free block
	SETZRO FLIST,(BLK)	; Block may become the end of a list
	SETOB T1,MRGFLG		; Say common size and merging possible
	CALLRET RETB

; RETB	Return the block to some list. Called with NOINT & FRELCK set.

;BLK/	(Extended) Block	; with FSIZE & FNEXT
;SIZ/	Size of the block	; Excluding header
;T1/	UserCall Flag		; Non-zero says to create a slot in
;				  INTBLK for this size
;
;	CALL RETB
;Ret+1:	Always

RETB:	PUSH P,T1		; Save the flag
	CALL HASHX		; Get -1 or index to INTBLK
	POP P,T2		; Restore the flag
	JUMPL T1,RETB1		; Jump if there is no slot for this size
	JUMPE T2,RETB1		; Jump if not a user's block size
	MOVEI T2,INTBLK(T1)	; Pointer to the list head
	LOAD LST,FLIST,(T2)	; Pointer to the list itself
	STOR LST,FLIST,(BLK)	; Make block point to current tail
	STOR BLK,FLIST,(T2)	; Make head point to new front of list
	STOR SIZ,FSIZE,(T2)	; Be sure size is right in the table
	EXIT RETBX

RETB1:	PUSH P,LST		; RETLST wants this preserved
	MOVE LST,ODDBLK		; The appropriate (extended) list
	CALL SPUT		; Put block on that list
	MOVEM LST,ODDBLK	; New list with block on it
	POP P,LST

RETBX:	RET

; SPUT(Block, List)	Put a block on an address ordered list

;BLK/	(Extended) Block pointer  with FSIZE & FNEXT
;LST/	(Extended) List pointer
;
;	CALL SPUT
;Ret+1:	Always.  New list containing Block in LST

SPUT:	TEMP <CUR,SUC>
	TRNE LST,-1
	 JRST SPUT1		; Jump if not adding to null list
	STOR LST,FLIST,(BLK)	; Clear list pointer in block
	MOVE LST,BLK		; New list has only this block
	EXIT SPUTX		; Return LST as new list

SPUT1:	CAML BLK,LST		; Adding to front of list?
	 JRST SPUT2		; No. Search for right spot.
;	could check that FNEXT(BLK) <= LST
	STOR LST,FLIST,(BLK)	; Make BLK be first on the list
	MOVE SUC,LST		; Init so rest works
	MOVE LST,BLK		; Value to be returned
	MOVE CUR,BLK		; Current block on list
	JRST SPUT4

SPUT2:	SKIPA CUR,LST		; Start at beginning of list
SPUT3:	MOVE CUR,SUC		; Advance to next on list
	LOAD SUC,FLIST,(CUR)	; Get the successor to CUR
	SETSEC SUC,INTSEC	; Make extended address
	CAML BLK,SUC		; BLK must be below SUC
	TRNN SUC,-1		; or SUC must be (INTSEC,,) 0
				; (CUR is end of list)
	CAMG BLK,CUR		; and BLK must be above CUR
	 JRST SPUT3		; Not right place for insert

;	could check that FNEXT(CUR) <= BLK & FNEXT(BLK) <= SUC
	STOR SUC,FLIST,(BLK)	; Patch in BLK between CUR and SUC
	STOR BLK,FLIST,(CUR)

SPUT4:	LOAD T4,FNEXT,(BLK)	; Word address following BLK
	JUMPE T4,SPUT5		; Beware match on INTSEC,,0
	SETSEC T4,INTSEC	; Make extended address
	CAME T4,SUC		; Combine BLK and SUC ?
	 JRST SPUT5		; No
	LOAD T3,FSIZE,(BLK)	; SUC is real block, not INTSEC,,0
	LOAD T4,FSIZE,(SUC)	; end pointer
	ADDI T3,UBLKSZ(T4)	; Size of combined block
	STOR T3,FSIZE,(BLK)
	LOAD T4,FNEXT,(SUC)	; End of SUC
	STOR T4,FNEXT,(BLK)	; Is new end of combined block
	LOAD SUC,FLIST,(SUC)	; Successor of SUC is new SUC
	STOR SUC,FLIST,(BLK)	; and successor of combined BLK

SPUT5:	LOAD T3,FNEXT,(CUR)	; Address following CUR
	SETSEC T3,INTSEC	; Make extended address
	CAME T3,BLK		; Combine CUR and BLK?
	 EXIT SPUTX
	LOAD T3,FSIZE,(CUR)
	LOAD T4,FSIZE,(BLK)
	ADDI T3,UBLKSZ(T4)
	STOR T3,FSIZE,(CUR)	; Set size of combined block
	LOAD T4,FLIST,(BLK)	; Successor of BLK
	STOR T4,FLIST,(CUR)	; Is successor of combined block
	LOAD T4,FNEXT,(BLK)	; Get thing following BLK in memory
	STOR T4,FNEXT,(CUR)	; That is what follows compbined block
SPUTX:	RESTORE
	RET

;GETBBK		Assign biggest block of free storage

; 0 may be returned as a value meaning no space was available.
;  The caller is expected to cope with this situation.

;T1/	Minimum acceptable size
;T2/	Maximum usefull size
;
;	CALL GETBBK
;Ret+1:	Always.  T1 has 0 or or size,,pointer
;***** N.B.:  T1 does not have an extended address *****

GETBBK::DMOVEM T1,T3		; Place args for call via LCKCAL
	MOVEI T1,FRELCK		; The lock to set
	XMOVEI T2,[PUSH P,BLK	; Save ACs which will be
		PUSH P,SIZ	; Used as globals
		PUSH P,LST
		MOVE SIZ,T2	; Max size
		CALL GETBB0	; Do the work
		MOVE T1,BLK	; Value for caller
		POP P,LST
		POP P,SIZ
		POP P,BLK
		RET]
	CALLRET LCKCAL		; Call the function with the lock set

;GETBB0		Workhorse for the above. Called with NOINT & FRELCK set.
;
;T1/	Min. size
;SIZ/	Max. size, excluding header
;FRELCK set
;
;	CALL GETBB0
;Ret+1:	Always.  BLK has 0 or size,,pointer

GETBB0:	LOCAL <MINSIZ>
	MOVEM T1,MINSIZ
	CALL GETBK0		; Use normal GETBLK routine
	JUMPN T1,GETBBX		; Exit if we got the max. size block

	; Note that the fail return from GETBK0 indicates that a
	; garbage collect has happened and that all free blocks are
	; now either on the ODDBLK list or INTBLK+n.  Further, no
	; block on ODDBLK is greater than or equal to the MAXSIZ.

	MOVEI SIZ,0		; Init max size seen
	LOAD T2,FLIST,<+ODDBLK>	; Init pointer to first block
GETBB1:	JUMPE T2,GETBB2		; Jump if at end of list
	SETSEC T2,INTSEC	; Make extended address
	LOAD T3,FSIZE,(T2)	; Get size of current block
	CAMLE T3,SIZ		; Bigger than seen before?
	 MOVE SIZ,T3		; Yes.  Save max.
	LOAD T2,FLIST,(T2)	; Point to next block
	JRST GETBB1

GETBB2:	CAMGE SIZ,MINSIZ	; Is the biggest block acceptable?
	 JRST GETBB9		; No.  Tell caller.
	CALL GETBK0
	JUMPN BLK,GETBBX	; Return if all went well.
	INBUG(CHK,<GETBK2: ODDBLK list fouled>,INTFR7)
GETBB9:	SETZB BLK,SIZ		; Failure indication
GETBBX:	HRL BLK,SIZ		; Place size for caller
	RESTORE
	RET

; GETBLK	Assign a block of free storage

; 0 may be returned as a value meaning no space was available.
;  The caller is expected to cope with this situation.

;T1/	Size
;
;	CALL GETBLK
;Ret+1:	Always. 0 or Extended Pointer to block in T1

GETBLK::MOVE T3,T1		; Place in right ac
	MOVEI T1,FRELCK		; Lock to set
	XMOVEI T2,[PUSH P,BLK	; Save ACs which will be
		PUSH P,SIZ	; used globally
		PUSH P,LST
		MOVE SIZ,T1
		CALL GETBK0	; Do the work
		MOVE T1,BLK
		POP P,LST
		POP P,SIZ
		POP P,BLK
		RET]
	CALLRET LCKCAL		; Call routine with lock set

;GETBK0		Workhorse for above. Called with NOINT & FRELCK set.
;
;SIZ/	Size of block to be assigned, excluding header
;
;	CALL GETBK0
;Ret+1:	Always.  0 or Extended Pointer to block in BLK. Must save SIZ.


GETBK0:	JUMPG SIZ,GETBK1
	INBUG (HLT,<GETBK0: Bad block size request>,INTFR4)
	MOVEI SIZ,1		; Min size we ever hand out
GETBK1:	CAILE SIZ,INTFSZ-UBLKSZ	; Max size
	 INBUG (HLT,<GETBK1: Bad block size request>,INTFR5)
	CAIG SIZ,BSMALL		; Don't quantize Q heads etc.
	 JRST GETBK2
	ADDI SIZ,BSQUAN-1	; Round up
	ANDCMI SIZ,BSQUAN-1	; To nearest bigger multiple
GETBK2:	CALL GETB		; Get it from somewhere
	JUMPE BLK,GETBKX	; Couldn't get the block
	MOVNI T2,UBLKSZ(SIZ)	; Size of block we will hand out
	ADDM T2,INTFSP		; Decrease amt of free space available
	CALL HASH		; Get a random number
	STOR T1,UHASH,(BLK)	; Check this when block returned
	STOR SIZ,USIZE,(BLK)	; Set the block size
	XMOVEI BLK,UBLKSZ(BLK)	; Value is user area of the block
GETBKX:	RET

; GETB		Get a block from somewhere. Called with NOINT & FRELCK set.

;SIZ/	Size, excluding header
;
;	CALL GETB
;Ret+1:	Always.   0 or (Extended) Pointer to block in BLK

GETB:	SETZ T1,		; Don't create a slot
	CALL HASHX		; Get index to INTBLK table
	JUMPL T1,GETB2		; Not in table.
	MOVEI T3,INTBLK(T1)	; Address of list head
	LOAD T4,FLIST,(T3)	; Pointer to list of blocks of this size
	JUMPE T4,GETB2		; None.  Try something else.
	SETSEC T4,INTSEC	; Make extended address
	LOAD T2,FLIST,(T4)	; Successor of 1st block on list
	STOR T2,FLIST,(T3)	; Is now first thing on list
	SKIPA BLK,T4		; This block is the result
GETB2:	  CALL GCARVE		; Look elsewhere for a block
	RET

; GCARVE	Carver a block of the required size from an odd block.
;		Called with NOINT & FRELCK set.

;SIZ/	Size, excluding header
;
;	CALL GCARVE
;Ret+1:	Always.   0 or (Extended) Pointer to block in BLK.

GCARVE:	TEMP <PRV>
	SKIPN ODDBLK		; Are there any odd blocks?
	 JRST GCARV4		; No.  Try something else
	XMOVEI PRV,ODDBLK	; Address of pointer to odd block list
	LOAD BLK,FLIST,(PRV)	; Pointer to first odd block

GCARV1:	SETSEC BLK,INTSEC	; Make extended address
	LOAD T2,FSIZE,(BLK)	; Get size of this odd block
	CAME T2,SIZ		; Same as required?
	 JRST GCARV2		; No.  Keep looking.
	LOAD T3,FLIST,(BLK)	; Pointer to block after this one
	STOR T3,FLIST,(PRV)	; Is new successor to one before this
	EXIT GCARVX

GCARV2:	CAIG T2,FBLKSZ+UBLKSZ(SIZ)	; Min we can carve succesffully
	 JRST GCARV3		; Not big enough.
	LOAD T3,FLIST,(BLK)	; Successor of this one
	STOR T3,FLIST,(PRV)	; Snip it out
	CALL CSPLIT		; Split into required plus extra
	EXIT GCARVX

GCARV3:	MOVE PRV,BLK
	LOAD BLK,FLIST,(PRV)	; Move to next odd block
	JUMPG BLK,GCARV1	; And look at it
GCARV4:	CALL BULKCV		; Above failed.  Try bulk storage
GCARVX:	RESTORE
	RET

; CSPLIT		Split an odd block into required size plus extra.
;			Called with NOINT & FRELCK set.
;BLK/	(Extended) BLK
;SIZ/	Required size, excluding header
;
;	CALL CSPLIT
;Ret+1:	Always.  Extended pointer to block of requird size in BLK

CSPLIT:	LOAD T3,FSIZE,(BLK)	; Get size of block to be split
	MOVE T1,BLK		; Get whole block
	ADDI T1,UBLKSZ(SIZ)	; Start of fragment
	SETSEC T1,INTSEC	; Make extended address
	STOR SIZ,FSIZE,(BLK)	; Store size of block to be returned
	SUBI T3,UBLKSZ(SIZ)	; Size of fragment
	STOR T3,FSIZE,(T1)	; Store size of fragment
	LOAD T4,FNEXT,(BLK)	; Block following this in memory
	STOR T4,FNEXT,(T1)
	PUSH P,BLK
	PUSH P,SIZ
	MOVE BLK,T1
	SETZ T1,		; Don't enter this in INTBLK table
	CALL RETB		; Return the fragment to free area
	POP P,SIZ
	POP P,BLK
	RET

; BULKCV		Carve block out of bulk storage.
;			Called with NOINT & FRELCK set.
;SIZ/	Size required, excluding header
;
;	CALL BULKCV
;Ret+1:	Always.  BLK has 0 or extended pointer to block

BULKCV:	MOVE T2,BULKND		; Current end of free storage
	SUB T2,BULKST		; Compute current length
	CAIE T2,UBLKSZ(SIZ)	; Exactly what we need?	(-1)
	 JRST BULKC1		; No.
	MOVEI BLK,0		;			Lose last word
	EXCH BLK,BULKST		; Get beginning of block to return
	SETZM BULKND		; and cancel bulk area
	EXIT BULKCX

BULKC1:	MOVE BLK,BULKST		; Start of what's left
	MOVEI T3,UBLKSZ(SIZ)	; What is needed
	CAIG T2,FBLKSZ+1(T3)	; Big enough to carve?	(-1)
	 JRST BULKC2		; No.
	ADDM T3,BULKST		; Remove from bulk area
	EXIT BULKCX

BULKC2:	JUMPE T2,BULKC3		; Jump if nothing at all left
	STOR T2,FSIZE,(BLK)	; Convert what is left into a block
	MOVE T3,BULKND		; Current End (extended)
	ADDI T3,1		; Next location there after
	STOR T3,FNEXT,(BLK)	; Fix up the block to be returned
	SETZ T1,		; Don't create a INTBLK Slot for it
	CALL RETB		; Return the piece
	SETZM BULKST		; Cancel bulk storage
	SETZM BULKND

BULKC3:	SKIPN MRGFLG		; Merging return blocks possible?
	 JRST BULKC4		; No.  Try something else.
	CALL GC			; Yes. Garbage collect. (Save SIZ)
	CALL GETB		; Assign the block
	EXIT BULKCX

BULKC4:;;Code to get more space from user.... (cannot do this - INTSEC)
	;;MOVEI T1,20		; This many pages
	;;CALL GETFSP		; Get the free space from him
	;;MOVEM T1,BULKST	; New bulk start
	;;ADDI T1,17777		; Compute end
	;;MOVEM T1,BULKND	; Set it
	;;MOVEI T1,17777
	;;ADDM T1,INTFSP	; Bump the amount available
	;;CALL BULKCV		; Carve out a chunk
	;;;;;;;
	MOVE T1,TODCLK		; NOW
	CAMG T1,INTSVT		; OK  to give another typeout?
	 JRST BULKC5		; No.  Not yet.
	 INBUG (INF,<BULKCV: Free storage exhausted>,INTFR6)
	ADDI T1,↑D60000		; 1 minute interval
	MOVEM T1,INTSVT		; Next deadline
BULKC5:	SETOM INTSVR		; And request everybody to do it
	MOVEI T1,0		; None available.  Let caller handle it.
BULKCX:	RET

; GC		Garbage Collector
;		Called with NOINT & FRELCK set.
; Saves SIZ & LST

GC:	LOCAL <I>
	PUSH P,LST
	MOVEI LST,0
	EXCH LST,ODDBLK		; Get and clear odd block list
	MOVSI I,-NFIXED		; AOBJN pointer to INTBLK table

GC1:	MOVEI T1,INTBLK(I)	; Pointer to current list header
	LOAD T1,FLIST,(T1)	; Pointer to first block on list
	JUMPE T1,GC2		; Avoid overhead of LCOPY on null list
	SETSEC T1,INTSEC	; Make extended address
	CALL LCOPY		; Append T1 list to LST
GC2:	SETZM INTBLK(I)		; Nullify the list
	AOBJN I,GC1

	CALL RETLST		; Return (merge) all blocks there on
	SETZM MRGFLG		; No merge possible now.
	POP P,LST
	RESTORE
	RET

;LCOPY		Put blocks from one list on another.
;		Called with NOINT & FRELCK set.
; Saves SIZ; Kills BLK

;T1/	(Extended) Input list
;LST/	(Extended) Output list
;
;	CALL LCOPY
;Ret+1:	Always, LST has the resulting extended list.

LCOPY:	LOCAL <ILST>
	PUSH P,SIZ
	MOVEM T1,ILST

LCOPY1:	TRNN ILST,-1		; End of ILST reached?
	 JRST LCOPYX		; Yes.
	MOVE BLK,ILST		; First block on list
;	LOAD SIZ,FSIZE,(BLK)	; Its size (uses FSIZE)
	LOAD ILST,FLIST,(BLK)	; Get successor for next time
	SETSEC ILST,INTSEC	; Make extended address
	CALL SPUT
	JRST LCOPY1

LCOPYX:	POP P,SIZ
	RESTORE
	RET

; RETLST		Return a list of blocks to free area
;			Called with NOINT & FRELCK set.
;LST/	(Extended) List pointer
;
;	CALL RETLST
;
;Ret+1:	Always.  Clobbers LST; Saves BLK


RETLST:	PUSH P,BLK

RETLS1:	TRNN LST,-1		; End of list?
	 JRST RETLSX		; Yes.  Done.
	MOVE BLK,LST
	LOAD LST,FLIST,(BLK)	; Successor is what to do next time
	SETSEC LST,INTSEC	; Make extended address
	SETZ T1,		; Indicate not being returned by user
	CALL RETB		; Return first block on the list
	JRST RETLS1

RETLSX:	POP P,BLK
	RET



; CHKBLK(Block)		; See that the hash mark is still ok, etc.
;			; Guards against double RETBLKs etc
;BLK/	(Extended) Pointer to block
;SIZ/	Size of the block, excluding UBLKSZ header
;
;	CALL CHKBLK
;Ret+1:	Always.

; Note: Ought to remove first CAIL & -UBLKSZ from second

CHKBLK:	CAIL SIZ,UBLKSZ		; Min size block ever handed out
	CAIL SIZ,INTFSZ-UBLKSZ	; Max size block ever handed out
	 INBUG (HLT,<CHKBLK: Block size clobbered>,INTFR0)
	CALL HASH
	LOAD T2,UHASH,(BLK)	; Get the mark we left there
	CAME T1,T2		; Is it still there?
	 INBUG (HLT,<CHKBLK: Block hash clobbered>,INTFR1)
	RET

;HASHX		Given a block size, HASHX returns the index to INTBLK

;SIZ/	Size
;T1/	CreateFlag	; Non-0 to create slot if not there already
;
;	CALL HASHX
;Ret+1:	Always.  -1 or Index in T1. Saves SIZ.

HASHX:	LOCAL <FLAG>
	TEMP <I,L,Q,T>		; L must be I+1
	MOVEM T1,FLAG
	MOVSI T,-NFIXED		; Set to scan the table

HASHX1:	MOVE I,SIZ
	ADDI I,0(T)		; Add probe count
	IDIVI I,NFIXED		; Rem is the hash function
	MOVE I,I+1		; (to I and L)
	MOVEI L,INTBLK(I)	; Pointer to head of list
	LOAD Q,FSIZE,(L)	; Get size of blocks on this one
	JUMPN Q,HASHX2		; Jump if slot is in use
	JUMPE FLAG,HASHXM	; Return -1 if not supposed to create it
	STOR SIZ,FSIZE,(L)	; Create the list
	EXIT HASHXX

HASHX2:	CAMN Q,SIZ		; Size we are looking for?
	 EXIT HASHXX		; Yes.
	AOBJN T,HASHX1		; Probe again
HASHXM:	SETO T1,		; Fail
HASHXX:	RESTORE
	RET

; HASH		; Return a random number based on location and size

; This number is stored in the block header while the block is in the
; hands of the user.  When he returns the block, a check is made to see
; that it has not been clobbered.

;BLK/	(Extended) Block location
;SIZ/	Block size
;
;	CALL HASH
;Ret+1:	Always.  Hash value in T1

HASH:	MOVEI T1,25252(BLK)	; Flush section number and garble a bit
	IMULI T1,1234(SIZ)	; Mulitply by garbled length
	TSC T1,T1
	HRRZS T1
	RET

	TNXEND
	END